/*
 * KjDevice.cpp
 *
 *  Created on: Dec 6, 2011
 *      Author: kylintse
 */

#include "KjDevice.h"
#include "gdata.h"

KjDevice::KjDevice()
{
	m_semCommand=new QSemaphore(1);
}

KjDevice::~KjDevice()
{
	delete m_semCommand;
}

qint32 KjDevice::pointCount(PVType vt)
{
	QMap<qint32,quint32>* values;

	switch(vt)
	{
	case pvtAi:
		values=&m_ai;
		break;

	case pvtDi:
		values=&m_di;
		break;

	case pvtPi:
		values=&m_di;
		break;

	default:
		return -1;
	}

	return values->size();
}

bool KjDevice::addPoint(PVType vt,qint32 id)
{
	QMap<qint32,quint32>* values;

	switch(vt)
	{
	case pvtAi:
		values=&m_ai;
		break;

	case pvtDi:
		values=&m_di;
		break;

	case pvtPi:
		values=&m_di;
		break;

	default:
		return false;
	}

	if(values->contains(id))
		return false;

	values->insert(id,0);
	return true;
}

bool KjDevice::removePoint(PVType vt,qint32 id)
{
	QMap<qint32,quint32>* values;

	switch(vt)
	{
	case pvtAi:
		values=&m_ai;
		break;

	case pvtDi:
		values=&m_di;
		break;

	case pvtPi:
		values=&m_di;
		break;

	default:
		return false;
	}

	if(!values->contains(id))
		return false;

	values->remove(id);
	return true;
}

bool KjDevice::clearPoint(PVType vt)
{
	QMap<qint32,quint32>* values;

	switch(vt)
	{
	case pvtAi:
		values=&m_ai;
		break;

	case pvtDi:
		values=&m_di;
		break;

	case pvtPi:
		values=&m_di;
		break;

	default:
		return false;
	}

	values->clear();
	return true;
}

bool KjDevice::readPointValue(PVType vt,qint32 id,quint32& value)
{
	QMap<qint32,quint32>* values;

	switch(vt)
	{
	case pvtAi:
		values=&m_ai;
		break;

	case pvtDi:
		values=&m_di;
		break;

	case pvtPi:
		values=&m_di;
		break;

	default:
		return false;
	}

	if(!values->contains(id))
		return false;

	value=values->value(id);
	return true;
}

bool KjDevice::writePointValue(PVType vt,qint32 id,quint32 value)
{
	QMap<qint32,quint32>* values;

	switch(vt)
	{
	case pvtAi:
		values=&m_ai;
		break;

	case pvtDi:
		values=&m_di;
		break;

	case pvtPi:
		values=&m_di;
		break;

	default:
		return false;
	}

	if(!values->contains(id))
		return false;

	(*values)[id]=value;
	return true;
}

bool KjDevice::lockForCommand(int timeout)
{
	return true;// m_semCommand->tryAcquire(1,timeout);
}

void KjDevice::unLockForCommand()
{
//	return m_semCommand->release();
}

///////////////////////////////////////////////////////////////////////////////
// KjDeviceList
KjDeviceList::KjDeviceList()
{

}

KjDeviceList::~KjDeviceList()
{
	qDeleteAll(m_devices);
	delete m_semDatagram;

}

bool KjDeviceList::init()
{
	if(gMprDevice)
	{
		for(int i=1;i<32;i++)
			m_devices.insert(i,new KjDevice);
	}

	KjDevice* device;
	for(int i=0;i<gDeviceListParam.size();i++)
	{
		device=new KjDevice;

		for(int j=0;j<gDeviceListParam[i]->lstAi.size();j++)
			device->addPoint(pvtAi,gDeviceListParam[i]->lstAi[j]);

		for(int j=0;j<gDeviceListParam[i]->lstDi.size();j++)
			device->addPoint(pvtDi,gDeviceListParam[i]->lstDi[j]);

		for(int j=0;j<gDeviceListParam[i]->lstPi.size();j++)
			device->addPoint(pvtPi,gDeviceListParam[i]->lstPi[j]);

		m_devices.insert(gDeviceListParam[i]->id,device);
	}

	device=new KjDevice;

	for(int i=0;i<gbattery.lstMesureChnnlDesc.size();i++)
		device->addPoint(pvtAi,i+1);

	for(int i=0;i<gbattery.lstAlarmDesc.size();i++)
		device->addPoint(pvtDi,i+1);

	m_devices.insert(BAT_CPU_NUM,device);

	m_semDatagram=new QSemaphore(0);
	return true;
}

bool KjDeviceList::putDatagram(const QByteArray& data)
{
	m_lockDatagram.lock();
	if(m_datagrams.size()>DG_LIMIT)
	{
		m_lockDatagram.unlock();
		return false;
	}
	m_datagrams.enqueue(data);
	m_lockDatagram.unlock();
	m_semDatagram->release();
	return true;
}

bool KjDeviceList::getDatagram(QByteArray& data,int timeout)
{
	if(!m_semDatagram->tryAcquire(1,timeout))
	{
		return false;
	}

	m_lockDatagram.lock();
	data=m_datagrams.dequeue();
	m_lockDatagram.unlock();
	return true;
}

qint32 KjDeviceList::pointCount(quint32 devId,PVType vt)
{
	if(!m_devices.contains(devId))
		return false;

	return m_devices.value(devId)->pointCount(vt);
}

bool KjDeviceList::readPointValue(quint32 devId,PVType vt,qint32 id,quint32& value)
{
	if(!m_devices.contains(devId))
		return false;

	return m_devices.value(devId)->readPointValue(vt,id,value);
}

bool KjDeviceList::writePointValue(quint32 devId,PVType vt,qint32 id,quint32 value)
{
	if(!m_devices.contains(devId))
		return false;

	return m_devices.value(devId)->writePointValue(vt,id,value);
}

bool KjDeviceList::registerEventProcessor(const QString& processorId,const QList<quint32>& lstDev)
{
	m_lockEventProcessor.lockForWrite();

	if(m_eventProcessors.contains(processorId))
	{
		m_lockEventProcessor.unlock();
		return false;
	}

	KjEventProcessor* processor=new KjEventProcessor;
	processor->sem=new QSemaphore(0);
	processor->lstDev=lstDev;
	m_eventProcessors.insert(processorId,processor);
	m_lockEventProcessor.unlock();
	return true;
}

bool KjDeviceList::unregisterEventProcessor(const QString& processorId)
{
	m_lockEventProcessor.lockForWrite();

	if(!m_eventProcessors.contains(processorId))
	{
		m_lockEventProcessor.unlock();
		return false;
	}

	KjEventProcessor* processor=m_eventProcessors.value(processorId);
	qDeleteAll(processor->events);
	delete processor->sem;
	delete processor;
	m_eventProcessors.remove(processorId);
	m_lockEventProcessor.unlock();
	return true;
}

bool KjDeviceList::putEvent(quint32 devId,KjEventType type,qint32 code,
		quint32 timeOccur,const QByteArray& value)
{
	if(!m_devices.contains(devId))
		return false;

	bool bret=false;
	KjEvent* event;
	KjEventProcessor* processor;

	m_lockEventProcessor.lockForRead();

	QMap<QString,KjEventProcessor*>::const_iterator i=m_eventProcessors.constBegin();
	while(i!=m_eventProcessors.constEnd())
	{
		processor=i.value();
		if(processor->lstDev.contains(devId))
		{
			event=new KjEvent;
			event->devId=devId;
			event->type=type;
			event->code=code;
			event->timeOccur=timeOccur;
			event->value=value;
			processor->lock.lock();
			processor->events.enqueue(event);
			processor->lock.unlock();
			processor->sem->release(1);
			bret=true;
		}
		i++;
	}

	m_lockEventProcessor.unlock();

	return bret;
}

bool KjDeviceList::getEvent(const QString& processorId,quint32& devId,KjEventType& type,
		qint32& code,quint32& timeOccur,QByteArray& data,int timeout)
{
	m_lockEventProcessor.lockForRead();

	if(!m_eventProcessors.contains(processorId))
	{
		m_lockEventProcessor.unlock();
		return false;
	}

	KjEventProcessor* processor=m_eventProcessors.value(processorId);
	if(processor->sem->tryAcquire(1,timeout))
	{
		processor->lock.lock();
		KjEvent* event=processor->events.dequeue();
		processor->lock.unlock();
		devId=event->devId;
		type=event->type;
		code=event->code;
		timeOccur=event->timeOccur;
		data=event->value;
		delete event;
		m_lockEventProcessor.unlock();
		return true;
	}

	m_lockEventProcessor.unlock();
	return false;
}

bool KjDeviceList::registerCommandProcessor(quint32 processorId,
		const QList<quint32>& lstDev,quint32 ethIdCare/*=2*/)
{
	m_lockCmdProcessor.lockForWrite();

	if(m_cmdProcessors.contains(processorId))
	{
		m_lockCmdProcessor.unlock();
		return false;
	}

	KjCommandProcessor* processor=new KjCommandProcessor;
	processor->sem=new QSemaphore(0);
	processor->ethIdCare=ethIdCare;
	processor->lstDev=lstDev;
	m_cmdProcessors.insert(processorId,processor);
	m_lockCmdProcessor.unlock();
	return true;
}

bool KjDeviceList::unRegisterCommandProcessor(quint32 processorId)
{
	m_lockCmdProcessor.lockForWrite();

	if(!m_cmdProcessors.contains(processorId))
	{
		m_lockCmdProcessor.unlock();
		return false;
	}

	KjCommandProcessor* processor=m_cmdProcessors.value(processorId);
	delete processor->sem;
	delete processor;
	m_cmdProcessors.remove(processorId);
	m_lockCmdProcessor.unlock();
	return true;
}

bool KjDeviceList::regiserCommandNotifyProcessor(const QString& source)
{
	m_lockCmdNotifyProcessor.lockForWrite();

	if(m_cmdNotifyProcessors.contains(source))
	{
		m_lockCmdNotifyProcessor.unlock();
		return false;
	}

	KjCommandNotifyProcessor* processor=new KjCommandNotifyProcessor;
	processor->sem=new QSemaphore(0);
	m_cmdNotifyProcessors.insert(source,processor);
	m_lockCmdNotifyProcessor.unlock();
	return true;
}

bool KjDeviceList::unRegisterCommandNotifyProcessor(const QString& source)
{
	m_lockCmdNotifyProcessor.lockForWrite();

	if(!m_cmdNotifyProcessors.contains(source))
	{
		m_lockCmdNotifyProcessor.unlock();
		return false;
	}

	KjCommandNotifyProcessor* processor=m_cmdNotifyProcessors.value(source);
	delete processor->sem;
	delete processor;
	m_cmdNotifyProcessors.remove(source);
	m_lockCmdNotifyProcessor.unlock();
	return true;
}

bool KjDeviceList::putCommand(const QString& source,quint32 ethId,quint32 serverType,quint32 devId,
		const QByteArray& data,qint8 needNotify,int timeout)
{
	bool bret=false;
	if(!m_devices.contains(devId))
		return bret;
	if(!m_devices.value(devId)->lockForCommand(timeout))
		return false;
	QMap<quint32,KjCommandProcessor*>::const_iterator i=m_cmdProcessors.constBegin();

	KjCommandProcessor* processor;
	KjCommand* cmd;

	while(i!=m_cmdProcessors.constEnd())
	{
		processor=i.value();
		if(processor->lstDev.contains(devId))
		{
			if((processor->ethIdCare==2)||(processor->ethIdCare==ethId))
			{
				cmd=new KjCommand;
				cmd->source=source;
				cmd->serverType=serverType;
				cmd->devId=devId;
				cmd->data=data;
				cmd->needNotify=needNotify;
				cmd->notifyFinish=false;
				cmd->startTime=QDateTime::currentDateTime();
				processor->lockCmd.lock();
				processor->commands.enqueue(cmd);
				processor->lockCmd.unlock();
				processor->sem->release();
				bret=true;
			}
		}
		i++;
	}

	return bret;
}

bool KjDeviceList::getCommand(quint32 processorId,QString& source,quint32& serverType,
		quint32& devId,QByteArray& data,int timeout)
{
	if(!m_cmdProcessors.contains(processorId))
		return false;

	KjCommandProcessor* processor=m_cmdProcessors.value(processorId);
	if(!processor->sem->tryAcquire(1,timeout))
		return false;

	processor->lockCmd.lock();
	KjCommand* cmd=processor->commands.dequeue();
	processor->lockCmd.unlock();
	source=cmd->source;
	serverType=cmd->serverType;
	devId=cmd->devId;
	data=cmd->data;

	if(cmd->needNotify)
	{
		m_lockCommandExcuting.lockForWrite();
		m_lstCommandExcuting.append(cmd);
		m_lockCommandExcuting.unlock();
	}
	else
	{
		// Since in putCommand function,we already check for the existence of the
		// device with devId,it's not necessary doing it here.
		m_devices.value(devId)->unLockForCommand();
		delete cmd;
	}

	return true;
}

bool KjDeviceList::putCommandNotify(const QString& source,quint32 devId,const QByteArray& data,
		quint8 notifyFinish)
{
	m_lockCommandExcuting.lockForRead();
	KjCommand* cmd;

	for(int i=0;i<m_lstCommandExcuting.size();i++)
	{
		cmd=m_lstCommandExcuting.value(i);
		if(cmd->source==source&&cmd->devId==devId)
		{
			m_devices.value(devId)->unLockForCommand();
			if(notifyFinish)
				m_lstCommandExcuting.removeAt(i);
			m_lockCommandExcuting.unlock();

			m_lockCmdNotifyProcessor.lockForRead();
			if(!m_cmdNotifyProcessors.contains(source))
			{
				m_lockCmdNotifyProcessor.unlock();
				delete cmd;
				return false;
			}

			cmd->notifyFinish=notifyFinish;
			cmd->dataNotify=data;
			m_cmdNotifyProcessors.value(source)->notifies.enqueue(cmd);
			m_cmdNotifyProcessors.value(source)->sem->release();
			m_lockCmdNotifyProcessor.unlock();
			return true;
		}
	}

	m_lockCommandExcuting.unlock();

	return false;
}

bool KjDeviceList::getCommandNotify(const QString& source,quint32& devId,QByteArray& data,
		int timeout,quint8& notifyFinish)
{
	m_lockCmdNotifyProcessor.lockForRead();
	if(!m_cmdNotifyProcessors.contains(source))
	{
		m_lockCmdNotifyProcessor.unlock();
		return false;
	}

	if(!m_cmdNotifyProcessors.value(source)->sem->tryAcquire(1,timeout))
	{
		m_devices.value(devId)->unLockForCommand();
		m_lockCmdNotifyProcessor.unlock();
		return false;
	}

	m_cmdNotifyProcessors.value(source)->lockNotify.lock();
	KjCommand* cmd=m_cmdNotifyProcessors.value(source)->notifies.dequeue();
	if((!cmd->notifyFinish)||(m_cmdNotifyProcessors.value(source)->notifies.size()))
		notifyFinish=0;
	else
		notifyFinish=1;
	m_cmdNotifyProcessors.value(source)->lockNotify.unlock();
	m_lockCmdNotifyProcessor.unlock();
	devId=cmd->devId;
	data=cmd->dataNotify;
	if(notifyFinish)
		delete cmd;
	return true;
}

